package com.cloudbroker.bcs.platform.ltb.middle.aspect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import com.cloudbroker.bcs.common.annotation.*;
import com.cloudbroker.bcs.platform.ltb.bizapi.constants.type.UserType;
import com.cloudbroker.bcs.platform.ltb.bizapi.service.base.vo.HSLTBBaseVO;
import com.cloudbroker.bcs.platform.ltb.middle.entity.UserEntity;
import com.cloudbroker.bcs.platform.ltb.middle.service.UserService;
import com.cloudbroker.bcs.platform.ltb.middle.util.MD5Util;
import com.cloudbroker.bcs.platform.ltb.middle.util.UserTokenUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import com.cloudbroker.bcs.common.constants.LogFields;
import com.cloudbroker.bcs.common.exception.HSBCSErrInfoException;
import com.cloudbroker.bcs.common.util.PrintUtil;
import com.cloudbroker.bcs.common.util.VOUtil;
import com.cloudbroker.bcs.platform.ltb.bizapi.constants.HSBCPSErrorCodes;
import com.cloudbroker.bcs.platform.ltb.bizapi.constants.HSBCPSErrorCodes.ClientErr;
import com.cloudbroker.bcs.platform.ltb.common.constant.SysContants;
import com.cloudbroker.bcs.platform.ltb.common.constant.type.PlatformType;
import com.cloudbroker.bcs.platform.ltb.common.util.SessionHelper;
import com.cloudbroker.bcs.platform.ltb.middle.context.HSBCPSBizDataContext;
import com.cloudbroker.bcs.platform.ltb.middle.entity.Broker;
import com.cloudbroker.bcs.platform.ltb.middle.entity.Customer;
import com.cloudbroker.bcs.platform.ltb.middle.model.UserCache;

import javax.annotation.Resource;

public class AuthCheckAspect {

    @Resource
    private UserService userService;
    
    private static final String CLASS_NAME = AuthCheckAspect.class.getName();
    
    private static final Logger LOGGER = LoggerFactory.getLogger(CLASS_NAME);
    
    public AuthCheckAspect() {
        PrintUtil.printComponentLoaded(LOGGER, CLASS_NAME);
    }

    public void setRequest(ProceedingJoinPoint joinPoint){
//        Object retVal = null;
//        retVal = joinPoint.proceed();
//
//        Object[] args = joinPoint.getArgs();
//        HttpServletRequest request = null;
//        //通过分析aop监听参数分析出request等信息
//        for (int i = 0; i < args.length; i++) {
//            if (args[i] instanceof HttpServletRequest) {
//                request = (HttpServletRequest) args[i];
//            }
//        }
//        String user_token = request.getParameter("user_token");
//        //
//
//        request.setAttribute("");
    }
    
    public Object checkAuth(ProceedingJoinPoint pjp) throws Throwable {
        // 在Spring的环境里，signature就是MethodSignature
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        // 获取Method
        Method method = methodSignature.getMethod();
        //判断是否是游客+NoLogin注解
        UserCache identity = null;
        /**
         * 1.所有接口先校验是否是管理人员权限
         * 2.所有接口校验是否具有user_token信息
         * 3.若为游客需校验接口是否有nologin注解 若有则抛出需登录
         * */
        if (!method.isAnnotationPresent(NoToken.class)) {
            // 聚合class和method上的权限，并缓存
            RequiredPermissions requiredPermissionsAnn = method.getAnnotation(RequiredPermissions.class);
            String[] requiredPermissions = null;
            if (null != requiredPermissionsAnn) {
                requiredPermissions = requiredPermissionsAnn.value();
            }
            
            Object result = null;
            try {
                // 检查登录及权限
                try {
                    checkAndBindLoginInfo(pjp.getArgs(), requiredPermissions,
                            method.isAnnotationPresent(NoRefreshSession.class), method);

                } catch (HSBCSErrInfoException e) {
                    if (e.isErrorIn(HSBCPSErrorCodes.NOT_LOGGED, HSBCPSErrorCodes.NO_PERMISSION,
                            ClientErr.NO_SUCH_CLIENT, HSBCPSErrorCodes.ADMIN_PASSWORD_INVALID,
                            HSBCPSErrorCodes.VISITOR_NOT_LOGGED)) {
                        result = VOUtil.genErrorResultVo(e.getErrorNo());
                        return result;
                    }
                    throw e;
                }

                // 执行业务
                result = pjp.proceed();
            } finally {
                clearBinding();
            }
            return result;
        } else {
            // 执行业务
            return pjp.proceed();
        }
    }
    
    private void checkAndBindLoginInfo(Object[] args, String[] requiredPermissions, boolean noRefresh, Method method) {
        for (Object arg : args) {
            String user_type = "";
            String user_name = "";
            String admin_password = "";
            String sendercomp_id = "";
            String company_id = "";
            try {
                for(Method tmpmethod : arg.getClass().getMethods()){
                    switch (tmpmethod.getName()){
                        case "getUser_name":
                            user_name = (String)tmpmethod.invoke(arg);
                            break;
                        case "getUser_type":
                            user_type = (String)tmpmethod.invoke(arg);
                            break;
                        case "getAdmin_password":
                            admin_password = (String)tmpmethod.invoke(arg);
                            break;
                        case "getSendercomp_id":
                            sendercomp_id = (String)tmpmethod.invoke(arg);
                            break;
                        case "getCompany_id":
                            company_id = (String)tmpmethod.invoke(arg);
                            break;
                        default:
                            break;
                    }
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            if (String.valueOf(UserType.ADMIN.getVal()).equals(user_type)){
                //admin用户进入admin判断
                if (!adminOpVerify(user_name, MD5Util.encrypt(admin_password), sendercomp_id, company_id)){
                    throw new HSBCSErrInfoException(HSBCPSErrorCodes.ADMIN_PASSWORD_INVALID);
                }
            }else{
                UserCache sessionIdentity = checkSessionPermission((HSLTBBaseVO) arg, requiredPermissions,
                        noRefresh, method);
                bindUserInfo(sessionIdentity);
            }
            break;
        }
    }
    
    private void bindUserInfo(UserCache sessionIdentity) {
        if (SysContants.SESSION_USER_TYPE_CLIENT.equals(sessionIdentity.getUser_type())) {
            // 当前session是client用户
//            bindClientInfo(sessionIdentity.getCustomer(), sessionIdentity.getBroker(),
//                    sessionIdentity.getBrokerId(), sessionIdentity.getClient_id(),
//                    sessionIdentity.getPlatformType());
        }
    }
    
    private void bindClientInfo(Customer customer, Broker broker, Long brokerId, Long clientId, Integer platformType) {
        if (PlatformType.MANAGE.getValue().equals(platformType)) {
            if (broker == null) {
                throw new HSBCSErrInfoException(HSBCPSErrorCodes.NO_PERMISSION);
            }
            HSBCPSBizDataContext.setBroker(broker);
            HSBCPSBizDataContext.setTenantId(broker.getTenant_id());
        } else if (PlatformType.MOBILE.getValue().equals(platformType)) {
            if (customer == null) {
                throw new HSBCSErrInfoException(HSBCPSErrorCodes.NO_PERMISSION);
            }
            HSBCPSBizDataContext.setCustomer(customer);
//            HSBCPSBizDataContext.setTenantId(customer.getTenant_id());
        }
        // 保存用户对象到线程上下文
        HSBCPSBizDataContext.setBrokerId(brokerId);
        HSBCPSBizDataContext.setClientId(clientId);
        //重新加入到在线用户列表缓存
        if (customer != null) {
            SessionHelper.getInstance().addOneToBrokeyOnline(String.valueOf(brokerId),
                    String.valueOf(customer.getCustomer_id()));
        }
    }
    
    private UserCache checkSessionPermission(HSLTBBaseVO param, String[] requiredPermissions,
                                             boolean noRefresh, Method method) {
        
        // 获取携带的access_token
        String access_token = param.getUser_token();
        // 获取会话身份对象
        UserCache identity = UserTokenUtil.queryUserToken(access_token);
        if (null == identity) {
            //游客超时
            if (access_token.indexOf("visitor") != -1){
                throw new HSBCSErrInfoException(HSBCPSErrorCodes.VISITOR_NOT_LOGGED);
            }
            // 如果会话身份对象不存在则表示未登录或已超时
            throw new HSBCSErrInfoException(HSBCPSErrorCodes.NOT_LOGGED);
        }else if (String.valueOf(UserType.VISITOR.getVal()).equals(identity.getUser_type())){
            if (!method.isAnnotationPresent(NoLogin.class)
                    && !method.getDeclaringClass().isAnnotationPresent(NoLogin.class)){
                throw new HSBCSErrInfoException(HSBCPSErrorCodes.NOT_LOGGED);
            }
        } else {
//            // 先不检查client权限(包括大V和粉丝)
//            if (!SysContants.SESSION_USER_TYPE_CLIENT.equals(identity.getUser_type())) {
//                // 检查是否拥有需要的权限
//                if (!AuthUtil.hasPermission(identity.getPermissions(), requiredPermissions)) {
//                    throw new HSBCSErrInfoException(HSBCPSErrorCodes.NO_PERMISSION);
//                }
//            }
            if (!noRefresh) {
                // 刷新会话，重置超时时间
                UserTokenUtil.updateUserToken(access_token, identity);
//                SessionHelper.getInstance()
//                        .refreshSession(PlatformType.getByValue(String.valueOf(identity.getPlatformType())),
//                                access_token, identity);
            }
            // 保存access_token到线程上下文
            HSBCPSBizDataContext.setAccess_token(access_token);
            MDC.put(LogFields.ACCESS_TOKEN, access_token);
            // 注意，这里没做clear
            // 当前是放在com.cloudbroker.bcs.common.aspect.LogAspect.log(ProceedingJoinPoint)中clear
        }
        return identity;
    }
    
    //    private void checkAndBindClientInfo(ClientIdParam param, String[] requiredPermissions) {
    //        // 先检查session登录与权限
    //        SessionIdentity sessionIdentity = checkSessionPermission(param, requiredPermissions);
    //        // 如果当前session是client用户，检查参数的client并绑定到线程上
    //        if (SysContants.SESSION_USER_TYPE_CLIENT.equals(sessionIdentity.getUser_type())) {
    //            Long clientId = param.getClient_id();
    //            // 查找客户对象，不存在则抛异常
    //            ClientProfile client = clientProfileService.get(clientId, true);
    //            // 获取session上的用户
    //            String userId = String.valueOf(sessionIdentity.getCustomerId());
    //            // 检查session的user和参数的client是否是同一个
    //            if (!String.valueOf(clientId).equals(userId)) {
    //                LOGGER.error("session user(client) '" + userId + "' cannot operate param client: " + clientId);
    //                throw new HSBCSErrInfoException(HSBCPSErrorCodes.NO_PERMISSION);
    //            }
    //            // 保存客户对象到线程上下文
    //            HSBCPSBizDataContext.setClientProfile(client);
    //        }
    //    }
    
    private void clearBinding() {
        HSBCPSBizDataContext.removeClientProfile();
        HSBCPSBizDataContext.removeAccess_token();
    }

    /**
     * 管理人员操作校验
     * */
    private boolean adminOpVerify(String user_name, String password, String sendercomp_id, String company_id){
        String user_key = user_name + password + sendercomp_id + company_id;
        boolean isVerify = UserTokenUtil.adminLoginVerify(user_key, company_id, sendercomp_id);
        if (isVerify) return true;
        //操作数据库校验
        UserEntity verifyUser = userService.userLoginVerify(new UserEntity(user_name,
                String.valueOf(UserType.ADMIN.getVal()),sendercomp_id, company_id));
        if (verifyUser == null) return false;
        if (password.equals(verifyUser.getPassword())){
            //校验成功新建管理缓存
            UserTokenUtil.addAdminUserToken(user_key, company_id, sendercomp_id);

            return true;
        }
        return false;
    }

}

